import 'dart:io';
import 'dart:ui';
import 'package:im_app/im/uploadAndDownload/FileUploadService.dart';
import 'package:im_app/im/util/ImageUtil.dart';
import 'package:path/path.dart' as path;

import 'package:im_app/im/util/UiUtil.dart';

import '../dao/ImMessageDao.dart';
import '../dao/ImMessageSendDao.dart';
import '../dao/ImSessionDao.dart';
import '../pojo/ImPojo.dart';
import '../socket/dto/base/Resp.dart';
import '../socket/dto/business/Message.dart';
import '../socket/service/RpcSendDataService.dart';
import '../uploadAndDownload/UploadAndDownloadDto.dart';
import '../util/EmojiUtil.dart';
import '../util/FileUtil.dart';
import '../util/ImCacheFactory.dart';
import '../util/ImConstant.dart';
import '../util/ImSingleton.dart';
import '../util/Md5Util.dart';
import '../util/MessageUtil.dart';
import 'BaseService.dart';

class ImMessageSendService extends BaseService {

  // final _imMessageDao = ImMessageDao();
  // final _imMessageSendDao = ImMessageSendDao();
  // final _imSessionDao = ImSessionDao();
  // final _rpcSendDataService = RpcMessageService();

  Future<void> sendMessage(List<ImSession> imSessions, List<ImMessage> imMessages) async {
    var userId = ImSingleton.getInstance().session.userId;
    final imMessageDao = ImMessageDao();
    final imSessionDao = ImSessionDao();
    final rpcSendDataService = RpcMessageService();

    await imMessageDao.batchInserts(imMessages);
    await imSessionDao.batchUpdates(userId!, imSessions);
    var messageObjects = <MessageObject>[];
    for (var imMessage in imMessages) {
      String str = EmojiUtil.replaceEmoji(imMessage.content);
      imMessage.status = ImMessageStatus.sended;
      MessageObject messageObject = await MessageUtil.toMessageObject(imMessage);
      if (imMessage.messageType == MessageType.RichText) {
        messageObject.content = str;
      }
      messageObjects.add(messageObject);
    }
    OkListResp okListResp = await rpcSendDataService.sendMessages(messageObjects);

    _doSendSuccess(userId!, okListResp, imMessages);
  }

  /// 只发送消息，不进行本地处理
  Future<void> sendMessages(String userId, List<ImMessage> messages) async {
    OkListResp okListResp = await _sendMessageToServer(messages);
    await _doSendSuccess(userId, okListResp, messages);
  }

  Future<OkListResp> _sendMessageToServer(List<ImMessage> imMessages) async {
    final rpcSendDataService = RpcMessageService();
    List<MessageObject> messageObjects = <MessageObject>[];
    for (var imMessage in imMessages) {
      var messageObject = await MessageUtil.toMessageObject(imMessage);
      messageObjects.add(messageObject);
    }
    OkListResp okResp = await rpcSendDataService.sendMessages(messageObjects);
    return okResp;
  }

  Future<void> _doSendSuccess(String userId, OkListResp okListResp, List<ImMessage> messages) async {
    DateTime serverDateTime = okListResp.successDataTime;
    Map<String, ImMessage> clientIdMessageMap = Map.fromIterable(messages, key: (imMessage)=>imMessage.clientId, value: (imMessage)=>imMessage);

    final imSessionDao = ImSessionDao();
    final imMessageDao = ImMessageDao();
    final imMessageSendDao = ImMessageSendDao();

    List<ImSession> updateSessions = [];
    for (String serverInfo in okListResp.localSeqSet) {
      var ids = serverInfo.split(ImConstant.MESSAGE_SPLIT);
      String serverId = ids[1];
      String clientId = ids[0];
      String sessionId = ids[2];
      ImMessage imMessage = clientIdMessageMap[clientId]!;
      imMessage.serverId = serverId;
      imMessage.status = ImMessageStatus.sended;
      imMessage.serverReceiveTime = serverDateTime;
      imMessage.updateTime = DateTime.now();

      var session = imMessage.imSession;
      if (session == null) {
        if (imMessage.groupCode != null) {
          session = await imSessionDao.getByGroupCodeAndUserId(imMessage.groupCode!, userId);
        } else {
          session = await imSessionDao.getByUserId(userId, imMessage.fromUserId, imMessage.toUserId!);
        }
      }
      if (session != null && session.serverId == null) {
        session.serverId = sessionId;
        updateSessions.add(session);
      }
    }

    await imMessageDao.batchChangeMessageStatus(messages);
    await imSessionDao.batchUpdates(userId, updateSessions);

    for (ImMessage imMessage in messages) {
      if(!imMessage.isGroup) continue;
      if (imMessage.messageSendSet != null) continue;
      List<ImMessageSend> messageSendList = await imMessageSendDao.getByMessageId(imMessage.id!);
      imMessage.messageSendSet = messageSendList.toSet();
    }
    List<ImMessageSend> messageSends = [];
    List<List<ImMessageSend>> msList = messages.where((m)=>m.isGroup).where((m)=>m.messageSendSet != null).map((m)=>m
        .messageSendSet!.toList()).toList();
    msList.forEach((list)=>list.forEach((ms){
      if (ms.userId == userId) {
        ms.actionStatus = ImMessageActionStatus.readed;
        ms.updateTime = DateTime.now();
        messageSends.add(ms);
      }
    }));

    if (messageSends.isNotEmpty) {
      imMessageSendDao.batchChangeMessageSendActionStatus(userId, messageSends);
    }

    UiUtil.uiUpdateMessages(messages);

    for (var message in messages) {
      if (message.messageType.isFile()) {
        var fileUploadService = FileUploadService();
        fileUploadService.sendFile(message, (FileUploadResult uploadResult)=> uploadCallback(message, uploadResult));
      }
    }
  }

  Future<List<ImMessage>> buildMessages(ImSession imSession, String msg, List<ImMessage>? refMessages) async {
    if (msg == '') return [];
    var messages = [msg];

    var imMessages = <ImMessage>[];
    for(var obj in messages) {
      if (obj is String) {
        var content = obj.toString();
        var messageType = ImMessageType.Text;
        if (content.indexOf(ImConstant.MESSAGE_TYPE_PREFIX) >= 0) {
          messageType = ImMessageType.RichText;
        }
        var textMessage = await buildMessage(imSession, content, messageType, refMessages);
        imMessages.add(textMessage);
      } else {
        // todo other messageType
      }
    }

    return imMessages;
  }

  Future<ImMessage> buildMessage(ImSession imSession, String content, ImMessageType messageType, List<ImMessage>? refMessages) async  {
    var userId = ImSingleton.getInstance().session.userId!;
    var message = ImMessage(fromUserId: userId, messageType: messageType, content: content);
    message.clientId = ImSingleton.getInstance().seqCreator.create();
    message.groupCode = imSession.groupCode;
    message.currentUserId = userId;
    message.status = ImMessageStatus.created;
    message.actionStatus = ImMessageActionStatus.none;
    message.imSession = imSession;
    message.sessionId = imSession.id;
    message.serverReceiveTime = DateTime.now();
    message.fromImUser = (await ImCacheFactory.getInstance().getImUser(message.fromUserId))!;
    if (imSession.toUserId != null) {
      message.toImUser = await initToUser(imSession);
      message.toUserId = message.toImUser!.userId;
    } else {
      message.groupCode = imSession.groupCode;
      var imGroup = await ImCacheFactory.getInstance().getImGroup(imSession.currentUserId!, imSession.groupCode!);
      imSession.group = imGroup;
      message.fromGroupUser = imGroup!.currentGroupUser;
      if (message.fromGroupUser!.userName == null) {
        var user = await ImCacheFactory.getInstance().getImUser(message.fromGroupUser!.userId);
        message.fromGroupUser!.userName = user!.userName;
      }

      var messageSends = imGroup!.groupUserList?.map((gu)=>buildMessageSend(imGroup, gu)).toSet();
      message.messageSendSet = messageSends;
    }

    if (messageType.isFile()) {
      message.fileStatus = ImMessageFileStatus.created;

      var filePath = MessageUtil.getFilePath2(message);
      var file = File(filePath);
      var fileSize = await file.length();

      var md5 = fileSha512(filePath);
      var imageSize = await ImageUtil.getImageSize(filePath);
      var fileName = path.basename(filePath);
      var messageExt = MessageExt(remote: false, fileName: fileName, originalName: fileName, size: fileSize, fileUrl:
        filePath, md5: md5, imageWidth: imageSize[0], imageHeight: imageSize[1]);
      message.messageExt = messageExt;
      message.init();
    }

    if (refMessages != null) {
      message.refMessageIds = refMessages!.map((rm)=>rm.serverId).join(",");
      message.refType = ImMessageRefType.ref;
      message.refMessages = refMessages;
    }

    var latelyMessage = MessageUtil.buildLastedMessage(messageType, message.content!);
    imSession.latelyMessage = latelyMessage;
    imSession.updateTime = DateTime.now();

    return message;
  }

  ImMessageSend buildMessageSend(ImGroup group, ImGroupUser groupUser) {
    var messageSend = ImMessageSend(userId: groupUser.userId, status: ImMessageStatus.none);
    messageSend.actionStatus = ImMessageActionStatus.none;
    messageSend.fileStatus = ImMessageFileStatus.none;
    messageSend.status = ImMessageStatus.none;
    if (groupUser.userId == ImSingleton.getInstance().session.userId) {
      messageSend.status = ImMessageStatus.created;
    }
    return messageSend;
  }

  Future<ImUser> initToUser(ImSession imSession) async {
    if (imSession.toUserId != null) {
      var imUser = imSession.toUser?? (await ImCacheFactory.getInstance().getImUser(imSession.toUserId!));
      return imUser!;
    }
    throw Exception("不是p2p会话");
  }

  uploadCallback(ImMessage message, FileUploadResult uploadResult) async {
    if (uploadResult.fileSize > 0) {
      var progress = uploadResult.fileUploadSize * 1.0 / uploadResult.fileSize;
      print('[upload file callback], ${uploadResult.fileUploadSize}/${uploadResult.fileSize}/$progress');
      UiUtil.uiUpdateFileUploadProgress(message, progress);
    }
  }
}